﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using Jillzhang.GifUtility;

namespace System
{
    public static class GDIHelper
    {
        #region Common
        public static string TempPath
        {
            get
            {
                string path = System.Configuration.ConfigurationManager.AppSettings["GDITempPath"];
                if (path == null)
                {
                    path = App.CombinePath(@"\temp\");
                }
                return path;
            }
        }

        /// <summary>
        /// 会产生graphics异常的PixelFormat
        /// </summary>
        private static PixelFormat[] indexedPixelFormats = { PixelFormat.DontCare, PixelFormat.Undefined, PixelFormat.Format16bppArgb1555, PixelFormat.Format1bppIndexed, PixelFormat.Format4bppIndexed, PixelFormat.Format8bppIndexed };

        public static void CheckPixelFormat(ref Image srcImage)
        {
            var pixelFormat = srcImage.PixelFormat;
            //如果原图片是索引像素格式之列的，则需要转换
            if (!indexedPixelFormats.Any(p => p.Equals(pixelFormat)))
            {
                return;
            }

            var bmp = new Bitmap(srcImage.Width, srcImage.Height, PixelFormat.Format32bppArgb);
            using (var g = Graphics.FromImage(bmp))
            {
                //g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                //g.SmoothingMode = SmoothingMode.HighQuality;
                //g.CompositingQuality = CompositingQuality.HighQuality;
                g.DrawImage(srcImage, 0, 0);
            }
            srcImage = bmp;
        }

        public static bool IsGif(Image srcImage)
        {
            return srcImage.RawFormat.Guid == ImageFormat.Gif.Guid;
        }

        /// <summary>
        /// 按等比例缩小
        /// </summary>
        /// <param name="maxSize">需要缩小到的大小</param>
        /// <param name="srcSize">原始大小</param>
        /// <returns>返回按等比例缩小后的大小</returns>
        public static SizeF GetProportionSize(SizeF maxSize, SizeF srcSize)
        {
            float w = 0F, h = 0F;
            float sw = srcSize.Width, sh = srcSize.Height;
            float mw = maxSize.Width, mh = maxSize.Height;
            if (sw < mw && sh < mh)
            {
                w = sw;
                h = sh;
            }
            else if ((sw / sh) > (mw / mh))
            {
                w = maxSize.Width;
                h = (w * sh) / sw;
            }
            else
            {
                h = maxSize.Height;
                w = (h * sw) / sh;
            }
            return new SizeF(w, h);
        }

        public static Bitmap Crop(Image srcImage, Rectangle rect)
        {
            if (IsGif(srcImage))
            {
                string path = string.Format("{0}{1}.gif", TempPath, Guid.NewGuid().ToString()),
                    outPath = string.Format("{0}{1}.gif", TempPath, Guid.NewGuid().ToString());
                srcImage.Save(path);
                GifHelper.Crop(path, rect, outPath);
                return new Bitmap(outPath);
            }

            Bitmap destImage = new Bitmap(rect.Width, rect.Height);
            using (var g = Graphics.FromImage(destImage))
            {
                g.DrawImage(srcImage, new Rectangle(Point.Empty, destImage.Size), rect, GraphicsUnit.Pixel);
            }
            return destImage;
        }

        /// <summary>
        /// 根据图片exif调整方向
        /// </summary>
        /// <param name="sm"></param>
        /// <returns></returns>
        public static void ExifRotate(Image img)
        {
            var exif = img.PropertyItems;
            byte orien = 0;
            var item = exif.Where(m => m.Id == 274).ToArray();
            if (item.Length > 0)
            {
                orien = item[0].Value[0];
            }
            switch (orien)
            {
                case 2:
                    img.RotateFlip(RotateFlipType.RotateNoneFlipX);//horizontal flip
                    break;
                case 3:
                    img.RotateFlip(RotateFlipType.Rotate180FlipNone);//right-top
                    break;
                case 4:
                    img.RotateFlip(RotateFlipType.RotateNoneFlipY);//vertical flip
                    break;
                case 5:
                    img.RotateFlip(RotateFlipType.Rotate90FlipX);
                    break;
                case 6:
                    img.RotateFlip(RotateFlipType.Rotate90FlipNone);//right-top
                    break;
                case 7:
                    img.RotateFlip(RotateFlipType.Rotate270FlipX);
                    break;
                case 8:
                    img.RotateFlip(RotateFlipType.Rotate270FlipNone);//left-bottom
                    break;
                default:
                    break;
            }
        }
        #endregion

        #region Thumbnail
        public static void MakeThumbnail(string inImgPath, ThumbnailSetting setting)
        {
            using (var srcImage = Image.FromFile(inImgPath))
            {
                if (IsGif(srcImage))
                {
                    SizeF size = GDIHelper.GetProportionSize(new SizeF(setting.Width, setting.Height), srcImage.Size);
                    GifHelper.GetThumbnail(inImgPath, (double)size.Width / srcImage.Width, setting.SavePath);
                    return;
                }

                var destImage = GetThumbnail(srcImage, new SizeF(setting.Width, setting.Height), setting.Mode);
                destImage.Save(setting.SavePath);
            }
        }

        public static Bitmap GetThumbnail(Image srcImage, SizeF thumSize, ThumbnailMode mode)
        {
            if (IsGif(srcImage))
            {
                string path = string.Format("{0}{1}.gif", TempPath, Guid.NewGuid().ToString()),
                    outPath = string.Format("{0}{1}.gif", TempPath, Guid.NewGuid().ToString());
                srcImage.Save(path);
                SizeF size = GDIHelper.GetProportionSize(thumSize, srcImage.Size);
                GifHelper.GetThumbnail(path, (double)size.Width / srcImage.Width, outPath);
                return new Bitmap(outPath);
            }

            RectangleF thumRect = new RectangleF(PointF.Empty, thumSize);
            RectangleF srcRect = new RectangleF(PointF.Empty, srcImage.Size);
            switch (mode)
            {
                case ThumbnailMode.Zoom:
                    thumRect.Size = GetProportionSize(thumSize, srcRect.Size);
                    break;
                case ThumbnailMode.ZoomWidth:
                    thumRect.Width = srcRect.Width * thumRect.Height / srcRect.Height;
                    break;
                case ThumbnailMode.ZoomHeight:
                    thumRect.Height = srcRect.Height * thumRect.Width / srcRect.Width;
                    break;
                case ThumbnailMode.Cut:
                    if (srcRect.Width / srcRect.Height > thumRect.Width / thumRect.Height)
                    {
                        srcRect.Width = srcRect.Height * thumRect.Width / thumRect.Height;
                        srcRect.X = (srcImage.Width - srcRect.Width) / 2;
                    }
                    else
                    {
                        srcRect.Height = srcRect.Width * thumRect.Height / thumRect.Width;
                        srcRect.Y = (srcImage.Height - srcRect.Height) / 2;
                    }
                    break;
            }
            Bitmap destImage = new Bitmap((int)thumRect.Width, (int)thumRect.Height);
            using (var g = Graphics.FromImage(destImage))
            {
                g.CompositingQuality = CompositingQuality.HighSpeed;
                g.InterpolationMode = InterpolationMode.Low;
                g.SmoothingMode = SmoothingMode.HighSpeed;
                g.DrawImage(srcImage, thumRect, srcRect, GraphicsUnit.Pixel);
            }
            return destImage;
        }
        #endregion

        #region WaterMark
        public static void MakeWaterMark(string inImgPath, WaterMarkSetting setting)
        {
            Image srcImage = Image.FromFile(inImgPath);
            switch (setting.Mode)
            {
                case WaterMarkMode.Text:
                    var font = new Font("Arial", setting.TextSize, GraphicsUnit.Pixel);
                    var color = ColorTranslator.FromHtml(setting.TextColor);
                    if (IsGif(srcImage))
                    {
                        RectangleF destRectF = new RectangleF(new PointF(setting.Margin, setting.Margin), new SizeF(font.Size, font.Size));
                        switch (setting.Alignment)
                        {
                            case ContentAlignment.TopLeft:
                                break;
                            case ContentAlignment.TopCenter:
                                destRectF.X = ((float)srcImage.Width - destRectF.Width) / 2;
                                break;
                            case ContentAlignment.TopRight:
                                destRectF.X = (float)srcImage.Width - destRectF.Width - destRectF.X;
                                break;
                            case ContentAlignment.MiddleLeft:
                                destRectF.Y = ((float)srcImage.Height - destRectF.Height) / 2;
                                break;
                            case ContentAlignment.MiddleCenter:
                                destRectF.X = ((float)srcImage.Width - destRectF.Width) / 2;
                                destRectF.Y = ((float)srcImage.Height - destRectF.Height) / 2;
                                break;
                            case ContentAlignment.MiddleRight:
                                destRectF.X = (float)srcImage.Width - destRectF.Width - destRectF.X;
                                destRectF.Y = ((float)srcImage.Height - destRectF.Height) / 2;
                                break;
                            case ContentAlignment.BottomLeft:
                                destRectF.Y = (float)srcImage.Height - destRectF.Height - destRectF.Y;
                                break;
                            case ContentAlignment.BottomCenter:
                                destRectF.X = ((float)srcImage.Width - destRectF.Width) / 2;
                                destRectF.Y = (float)srcImage.Height - destRectF.Height - destRectF.Y;
                                break;
                            case ContentAlignment.BottomRight:
                                destRectF.X = (float)srcImage.Width - destRectF.Width - destRectF.X;
                                destRectF.Y = (float)srcImage.Height - destRectF.Height - destRectF.Y;
                                break;
                        }
                        srcImage.Dispose();
                        GifHelper.SmartWaterMark(inImgPath, setting.Text, color, font, destRectF.X, destRectF.Y, setting.SavePath);
                        return;
                    }

                    GetWaterMark(ref srcImage, setting.Text, font, color, setting.Alignment, setting.Margin);
                    break;
                case WaterMarkMode.Image:
                    if (IsGif(srcImage))
                    {
                        RectangleF destRectF = new RectangleF(new PointF(setting.Margin, setting.Margin), setting.ImageMark.Size);
                        switch (setting.Alignment)
                        {
                            case ContentAlignment.TopLeft:
                                break;
                            case ContentAlignment.TopCenter:
                                destRectF.X = (srcImage.Width - destRectF.Width) / 2;
                                break;
                            case ContentAlignment.TopRight:
                                destRectF.X = srcImage.Width - destRectF.Width - destRectF.X;
                                break;
                            case ContentAlignment.MiddleLeft:
                                destRectF.Y = (srcImage.Height - destRectF.Height) / 2;
                                break;
                            case ContentAlignment.MiddleCenter:
                                destRectF.X = (srcImage.Width - destRectF.Width) / 2;
                                destRectF.Y = (srcImage.Height - destRectF.Height) / 2;
                                break;
                            case ContentAlignment.MiddleRight:
                                destRectF.X = srcImage.Width - destRectF.Width - destRectF.X;
                                destRectF.Y = (srcImage.Height - destRectF.Height) / 2;
                                break;
                            case ContentAlignment.BottomLeft:
                                destRectF.Y = srcImage.Height - destRectF.Height - destRectF.Y;
                                break;
                            case ContentAlignment.BottomCenter:
                                destRectF.X = (srcImage.Width - destRectF.Width) / 2;
                                destRectF.Y = srcImage.Height - destRectF.Height - destRectF.Y;
                                break;
                            case ContentAlignment.BottomRight:
                                destRectF.X = srcImage.Width - destRectF.Width - destRectF.X;
                                destRectF.Y = srcImage.Height - destRectF.Height - destRectF.Y;
                                break;
                        }
                        //否则会报错
                        srcImage.Dispose();
                        GifHelper.WaterMark(inImgPath, setting.ImageMark, destRectF.X, destRectF.Y, setting.SavePath);
                        return;
                    }

                    GetWaterMark(ref srcImage, setting.ImageMark, setting.Alignment, setting.Margin);
                    break;
            }
            srcImage.Save(setting.SavePath);
        }

        public static void GetWaterMark(ref Image srcImage, string text, Font textFont, Color textColor, ContentAlignment alignment, float margin)
        {
            CheckPixelFormat(ref srcImage);
            using (var g = Graphics.FromImage(srcImage))
            {
                g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
                g.CompositingQuality = CompositingQuality.HighQuality;
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                g.SmoothingMode = SmoothingMode.HighQuality;
                SizeF textSizeF = g.MeasureString(text, textFont);
                PointF destF = new PointF(margin, margin);
                switch (alignment)
                {
                    case ContentAlignment.TopLeft:
                        break;
                    case ContentAlignment.TopCenter:
                        destF.X = ((float)srcImage.Width - textSizeF.Width) / 2;
                        break;
                    case ContentAlignment.TopRight:
                        destF.X = (float)srcImage.Width - textSizeF.Width - destF.X;
                        break;
                    case ContentAlignment.MiddleLeft:
                        destF.Y = ((float)srcImage.Height - textSizeF.Height) / 2;
                        break;
                    case ContentAlignment.MiddleCenter:
                        destF.X = ((float)srcImage.Width - textSizeF.Width) / 2;
                        destF.Y = ((float)srcImage.Height - textSizeF.Height) / 2;
                        break;
                    case ContentAlignment.MiddleRight:
                        destF.X = (float)srcImage.Width - textSizeF.Width - destF.X;
                        destF.Y = ((float)srcImage.Height - textSizeF.Height) / 2;
                        break;
                    case ContentAlignment.BottomLeft:
                        destF.Y = (float)srcImage.Height - textSizeF.Height - destF.Y;
                        break;
                    case ContentAlignment.BottomCenter:
                        destF.X = ((float)srcImage.Width - textSizeF.Width) / 2;
                        destF.Y = (float)srcImage.Height - textSizeF.Height - destF.Y;
                        break;
                    case ContentAlignment.BottomRight:
                        destF.X = (float)srcImage.Width - textSizeF.Width - destF.X;
                        destF.Y = (float)srcImage.Height - textSizeF.Height - destF.Y;
                        break;
                }

                if (IsGif(srcImage))
                {
                    string path = string.Format("{0}{1}.gif", TempPath, Guid.NewGuid().ToString()),
                        outPath = string.Format("{0}{1}.gif", TempPath, Guid.NewGuid().ToString());
                    srcImage.Save(path);
                    GifHelper.SmartWaterMark(path, text, textColor, textFont, destF.X, destF.Y, outPath);
                    srcImage = new Bitmap(outPath);
                    return;
                }
                g.DrawString(text, textFont, new SolidBrush(textColor), destF);
            }
        }
        public static void GetWaterMark(ref Image srcImage, Bitmap waterMarkImage, ContentAlignment alignment, float margin)
        {
            if (srcImage.Width - margin < waterMarkImage.Width || srcImage.Height - margin < waterMarkImage.Height)
            {
                return;
            }

            CheckPixelFormat(ref srcImage);
            using (Graphics g = Graphics.FromImage(srcImage))
            {
                g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
                g.CompositingQuality = CompositingQuality.HighQuality;
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                g.SmoothingMode = SmoothingMode.HighQuality;
                RectangleF destRectF = new RectangleF(new PointF(margin, margin), waterMarkImage.Size);
                switch (alignment)
                {
                    case ContentAlignment.TopLeft:
                        break;
                    case ContentAlignment.TopCenter:
                        destRectF.X = (srcImage.Width - destRectF.Width) / 2;
                        break;
                    case ContentAlignment.TopRight:
                        destRectF.X = srcImage.Width - destRectF.Width - destRectF.X;
                        break;
                    case ContentAlignment.MiddleLeft:
                        destRectF.Y = (srcImage.Height - destRectF.Height) / 2;
                        break;
                    case ContentAlignment.MiddleCenter:
                        destRectF.X = (srcImage.Width - destRectF.Width) / 2;
                        destRectF.Y = (srcImage.Height - destRectF.Height) / 2;
                        break;
                    case ContentAlignment.MiddleRight:
                        destRectF.X = srcImage.Width - destRectF.Width - destRectF.X;
                        destRectF.Y = (srcImage.Height - destRectF.Height) / 2;
                        break;
                    case ContentAlignment.BottomLeft:
                        destRectF.Y = srcImage.Height - destRectF.Height - destRectF.Y;
                        break;
                    case ContentAlignment.BottomCenter:
                        destRectF.X = (srcImage.Width - destRectF.Width) / 2;
                        destRectF.Y = srcImage.Height - destRectF.Height - destRectF.Y;
                        break;
                    case ContentAlignment.BottomRight:
                        destRectF.X = srcImage.Width - destRectF.Width - destRectF.X;
                        destRectF.Y = srcImage.Height - destRectF.Height - destRectF.Y;
                        break;
                }

                if (IsGif(srcImage))
                {
                    string path = string.Format("{0}{1}.gif", TempPath, Guid.NewGuid().ToString()),
                        outPath = string.Format("{0}{1}.gif", TempPath, Guid.NewGuid().ToString());
                    srcImage.Save(path);
                    GifHelper.WaterMark(path, waterMarkImage, destRectF.X, destRectF.Y, outPath);
                    srcImage = new Bitmap(outPath);
                    return;
                }
                g.DrawImage(waterMarkImage, destRectF, new RectangleF(PointF.Empty, destRectF.Size), GraphicsUnit.Pixel);
            }
        }

        public static Bitmap ChangeOpacity(Image img, float opacityValue)
        {
            Bitmap bmp = new Bitmap(img.Width, img.Height); // Determining Width and Height of Source Image
            using (Graphics graphics = Graphics.FromImage(bmp))
            {
                ColorMatrix colormatrix = new ColorMatrix()
                {
                    Matrix33 = opacityValue
                };
                ImageAttributes imgAttribute = new ImageAttributes();
                imgAttribute.SetColorMatrix(colormatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
                graphics.DrawImage(img, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttribute);
            }
            return bmp;
        }
        #endregion

        #region 特效
        /// <summary>
        /// 渐变效果
        /// </summary>
        /// <param name="sourceBmp">图片源</param>
        /// <param name="colorHead">起点色(HTML格式)</param>
        /// <param name="colorTail">终点色(HTML格式)</param>
        /// <param name="angle"></param>
        public static Bitmap LinearGradientImage(Bitmap sourceBmp, string colorHead, string colorTail, float angle)
        {
            Bitmap destinationBmp = new Bitmap(sourceBmp.Width, sourceBmp.Height);
            using (var graph = Graphics.FromImage(destinationBmp))
            using (var brush = new LinearGradientBrush(new Rectangle(0, 0, destinationBmp.Width, destinationBmp.Height), ColorTranslator.FromHtml(colorHead), ColorTranslator.FromHtml(colorTail), angle))
            {
                graph.FillRectangle(brush, 0, 0, destinationBmp.Width, destinationBmp.Height);
            }
            return destinationBmp;
        }

        private const double PI = 3.1415926535897932384626433832795;
        private const double PI2 = 6.283185307179586476925286766559;
        /// <summary>
        /// 波形滤镜效果 正弦曲线Wave扭曲图片
        /// </summary>
        /// <param name="sourceBmp">图片源</param>
        /// <param name="bXDir">如果扭曲则选择为True</param>
        /// <param name="dMultValue">波形的幅度倍数，越大扭曲的程度越高，一般为3</param>
        /// <param name="dPhase">波形的起始相位，取值区间[0-2*PI)</param>
        /// <returns></returns>
        public static Bitmap TwistImage(Bitmap sourceBmp, bool bXDir, double dMultValue, double dPhase)
        {
            Bitmap destinationBmp = new Bitmap(sourceBmp.Width, sourceBmp.Height);
            // 将位图背景填充为白色
            using (var graph = Graphics.FromImage(destinationBmp))
            {
                graph.FillRectangle(new SolidBrush(Color.White), 0, 0, destinationBmp.Width, destinationBmp.Height);
            }
            double dBaseAxisLen = bXDir ? (double)destinationBmp.Height : (double)destinationBmp.Width;
            for (int i = 0; i < destinationBmp.Width; i++)
            {
                for (int j = 0; j < destinationBmp.Height; j++)
                {
                    double dx = 0;
                    dx = bXDir ? (PI2 * (double)j) / dBaseAxisLen : (PI2 * (double)i) / dBaseAxisLen;
                    dx += dPhase;
                    double dy = Math.Sin(dx);
                    // 取得当前点的颜色
                    int nOldX = 0, nOldY = 0;
                    nOldX = bXDir ? i + (int)(dy * dMultValue) : i;
                    nOldY = bXDir ? j : j + (int)(dy * dMultValue);
                    Color color = sourceBmp.GetPixel(i, j);
                    if (nOldX >= 0 && nOldX < destinationBmp.Width && nOldY >= 0 && nOldY < destinationBmp.Height)
                    {
                        destinationBmp.SetPixel(nOldX, nOldY, color);
                    }
                }
            }
            return destinationBmp;
        }
        #endregion
    }
}